package controller

import (
	"Software/global"
	"Software/middleware"
	"Software/model"
	"Software/response"
	"Software/webauthn"
	"github.com/gin-gonic/gin"
	"github.com/go-webauthn/webauthn/protocol"
	"github.com/go-webauthn/webauthn/webauthn"
)

// @Summary 开始注册
// @Produce  json
// @Param name query string false "用户名" maxlength(100)
// @Success 200 {object} model.Response "成功"
// @Failure 400 {object} model.Response "请求错误"
// @Router /auditor/beginRegister [get]
func BeginRegister(ctx *gin.Context) {
	name := ctx.Query("name")
	req := model.AdministratorReq{
		Name: name,
	}
	err := ctx.ShouldBind(&req)
	if err != nil {
		response.Response(ctx, 400, nil, "数据绑定失败")
		return
	}

	var auditor model.Auditor
	global.DB.Where("name = ?", req.Name).First(&auditor)
	if auditor.ID != 0 {
		response.Response(ctx, 400, nil, "此用户名已注册")
		return
	}

	challenge, _ := protocol.CreateChallenge()

	global.DB.Create(&model.ChallengeRecord{
		Challenge: challenge.String(),
		Name:      req.Name,
	})

	response.Response(ctx, 200, gin.H{
		"userid":      req.Name,
		"challengeid": challenge.String(),
	}, "获取成功")
}

// @Summary 完成注册
// @Produce  json
// @Param name query string false "用户名" maxlength(100)
// @Success 200 {object} model.Response "成功"
// @Failure 400 {object} model.Response "请求错误"
// @Router /auditor/finishRegister [post]
func FinishRegister(ctx *gin.Context) {
	w1 := w.GetWebauthn()
	var req protocol.CredentialCreationResponse
	err := ctx.ShouldBindJSON(&req)
	if err != nil {
		response.Response(ctx, 400, nil, "数据绑定失败")
		return
	}

	pcc, _ := req.Parse()

	var challenge model.ChallengeRecord
	global.DB.Where("challenge = ?", pcc.Response.CollectedClientData.Challenge).First(&challenge)

	err = pcc.Verify(challenge.Challenge, false, w1.Config.RPID, []string{pcc.Response.CollectedClientData.Origin})
	if err != nil {
		response.Response(ctx, 400, nil, "认证失败")
		return
	}

	credential, _ := webauthn.MakeNewCredential(pcc)
	global.DB.Create(&model.Auditor{
		Name:      challenge.Name,
		ShortID:   credential.ID,
		PublicKey: credential.PublicKey,
	})
}

// @Summary 开始登录
// @Produce  json
// @Param name query string false "用户名" maxlength(100)
// @Success 200 {object} model.Response "成功"
// @Failure 400 {object} model.Response "请求错误"
// @Router /auditor/beginLogin [get]
func BeginLogin(ctx *gin.Context) {
	name := ctx.Query("name")
	req := model.AdministratorReq{
		Name: name,
	}
	err := ctx.ShouldBind(&req)
	if err != nil {
		response.Response(ctx, 400, nil, "数据绑定失败")
		return
	}

	var auditor model.Auditor
	global.DB.Where("name = ?", req.Name).First(&auditor)
	if auditor.ID == 0 {
		response.Response(ctx, 400, nil, "此用户未注册过")
		return
	}

	challenge, _ := protocol.CreateChallenge()

	data := protocol.PublicKeyCredentialRequestOptions{
		AllowedCredentials: []protocol.CredentialDescriptor{
			{
				Type:            "public-key",
				CredentialID:    auditor.ShortID,
				Transport:       []protocol.AuthenticatorTransport{"usb", "nfc", "ble"},
				AttestationType: "",
			},
		},

		Challenge: challenge,
	}

	global.DB.Create(&model.ChallengeRecord{
		Name:      req.Name,
		Challenge: challenge.String(),
	})

	ctx.JSON(200, gin.H{
		"code": "200",
		"data": data,
		"msg":  "msg",
	})
}

// @Summary 完成登录
// @Produce  json
// @Param name query string false "用户名" maxlength(100)
// @Success 200 {object} model.Response "成功"
// @Failure 400 {object} model.Response "请求错误"
// @Router /auditor/finishLogin [post]
func FinishLogin(ctx *gin.Context) {
	var w1 = w.GetWebauthn()
	var req protocol.CredentialAssertionResponse
	err := ctx.ShouldBindJSON(&req)
	if err != nil {
		response.Response(ctx, 400, nil, "数据绑定失败")
		return
	}

	pca, _ := req.Parse()

	var challenge model.ChallengeRecord
	global.DB.Where("challenge = ?", pca.Response.CollectedClientData.Challenge).First(&challenge)

	var auditor model.Auditor
	global.DB.Where("name = ?", challenge.Name).First(&auditor)

	err = pca.Verify(challenge.Challenge, w1.Config.RPID, []string{pca.Response.CollectedClientData.Origin}, "", false, auditor.PublicKey)
	if err != nil {
		response.Response(ctx, 400, nil, "身份验证失败")
		return
	}

	token := middleware.CreateToken(auditor.Name)

	response.Response(ctx, 200, gin.H{
		"token": token,
	}, "身份验证成功")
}
